#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QThread>
#include <QProcess>

#include <windows.h>
#include <string>

#include "utils.h"
#include "constants.h"

namespace Jcr
{

Utils::Utils()
{

}

QString Utils::fileNameFromUrl(const QUrl& url)
{
    QFileInfo fi(url.path());
    return fi.fileName();
}

QString Utils::fileExtFromUrl(const QUrl& url)
{
    QFileInfo fi(url.path());
    return fi.suffix();
}

QString Utils::localFileName(const QString& brand, const QString& model, const QString& saveName)
{
    QString lfn = QString("%1/%2/%3/%4").arg(Jcr::IMAGEROOT).arg(brand).arg(model).arg(saveName);
    return QDir::toNativeSeparators(lfn);
}

QString Utils::makeDirectory(const QString& brand, const QString& model)
{
    const QString strImageRoot(Jcr::IMAGEROOT);
    QDir imageRootDir(strImageRoot);
    if (!imageRootDir.exists())
            imageRootDir.mkdir(strImageRoot);

    const QString strBrandDir = QString("%1/%2").arg(Jcr::IMAGEROOT).arg(brand);
    QDir brandDir(strBrandDir);
    if (!brandDir.exists()) {
        qDebug() << "brand directory \"" << strBrandDir << "\" doesn't exist, create it.";
        brandDir.mkdir(strBrandDir);
    }

    const QString strModelDir = QString("%1/%2/%3").arg(Jcr::IMAGEROOT).arg(brand).arg(model);
    QDir modelDir(strModelDir);
    if (!modelDir.exists()) {
        qDebug() << "model directory \"" << strModelDir << "\" doesn't exist, create it.";
        modelDir.mkdir(strModelDir);
    }

    QString fullPath = QString("%1/%2/%3").arg(Jcr::IMAGEROOT).arg(brand).arg(model);
    return fullPath;
}

bool Utils::fileExists(const QUrl &url, const QString& saveDir, const QString &saveName, QString* savePath)
{
    QFileInfo fi(url.path());
    QString ext = fi.suffix();
    QString newFullPath = QDir::toNativeSeparators(saveDir + "/" + saveName + "." + ext);
    if (savePath != nullptr)
        *savePath = newFullPath;
    bool isThere = QFile::exists(newFullPath);
    return isThere;
}

void Utils::runByThread(QObject *object)
{
    QThread* thread = new QThread();
    object->moveToThread(thread);
    QObject::connect(thread, SIGNAL(started()), object, SLOT(run()));
    QObject::connect(object, SIGNAL(finished()), thread, SLOT(terminate()));
    QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    QObject::connect(thread, SIGNAL(finished()), object, SLOT(deleteLater()));
    thread->start();
}

void Utils::write(const QString& file, const QByteArray& data)
{
    QFile outFile(file);
    outFile.open(QIODevice::WriteOnly);
    outFile.write(data);
    outFile.close();
}

QByteArray Utils::read(const QString& file)
{
    QFile inFile(file);
    inFile.open(QIODevice::ReadOnly);
    return inFile.readAll();
}

std::string Utils::gbk2Utf8(const std::string& strGBK)
{
    std::string strOutUTF8 = "";
    WCHAR * str1;
    int n = MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0);
    str1 = new WCHAR[n];
    MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, str1, n);
    n = WideCharToMultiByte(CP_UTF8, 0, str1, -1, NULL, 0, NULL, NULL);
    char * str2 = new char[n];
    WideCharToMultiByte(CP_UTF8, 0, str1, -1, str2, n, NULL, NULL);
    strOutUTF8 = str2;
    delete[]str1;
    str1 = NULL;
    delete[]str2;
    str2 = NULL;
    return strOutUTF8;
}
QUrl Utils::modelParametersUrl(const QUrl &modelUrl)
{
    QString paramsUrl = QString("%1/canshupeizhi.html").arg(modelUrl.toString());
    return QUrl(paramsUrl);
}

QUrl Utils::bigImageLink(const QUrl &thumbnailUrl)
{
    const QString path = thumbnailUrl.toString();
    const int dotPos = path.lastIndexOf(".");
    const QString newPath = path.mid(0, dotPos - 2) + path.mid(dotPos, path.length());
    return QUrl(newPath);
}

static bool compareStr(const QString& lhs, const QString& rhs)
{
    return lhs.compare(rhs, Qt::CaseInsensitive) == 0;
}

bool Utils::isGif(const QUrl& url)
{
    QFileInfo fi(url.toString());
    return compareStr(fi.suffix(), "gif");
}

bool Utils::isPicture(const QUrl& url)
{
    QFileInfo fi(url.toString());
    return compareStr(fi.suffix(), "jpg") || compareStr(fi.suffix(), "png");
}

void Utils::locate(const QString &path)
{
#ifdef Q_OS_WIN
    const QString strCmd("explorer.exe");
    const QStringList params = QStringList() << QString("/e,%1").arg(path);
    QProcess::startDetached(strCmd, params);
#endif
}

int Utils::dirCount(const QString& dir)
{
    QDir d(dir);
    return d.entryList(QDir::Dirs | QDir::NoDotAndDotDot).size();
}

int Utils::fileCount(const QString& dir)
{
    QDir d(dir);
    return d.entryList(QDir::Files | QDir::NoDotAndDotDot).size();
}

/*!
 * \brief GumboUtils::nodeText
 * \param node top node
 * \return if there's only one node left then it returns its text otherwise returns
 *  the combined text from sub nodes
 */
std::string GumboUtils::getNodeText(GumboNode *node)
{
    if (node->type == GUMBO_NODE_TEXT) {
        return std::string(node->v.text.text);
    } else if (node->type == GUMBO_NODE_ELEMENT &&
               node->v.element.tag != GUMBO_TAG_SCRIPT &&
               node->v.element.tag != GUMBO_TAG_STYLE) {
        std::string contents = "";
        GumboVector* children = &node->v.element.children;
        for (unsigned int i = 0; i < children->length; ++i) {
            const std::string text = getNodeText((GumboNode*) children->data[i]);
            if (i != 0 && !text.empty()) {
                contents.append(" ");
            }
            contents.append(text);
        }
        return contents;
    } else {
        return "";
    }
}

void GumboUtils::getAttributeValue(GumboNode *node, GumboTag tag, const char* attr, std::list<std::string>& values)
{
    if (!node || !attr)
        return;

    if (node->type != GUMBO_NODE_ELEMENT)
        return;

    if (node->v.element.tag == tag) {
        GumboAttribute* gAttr = gumbo_get_attribute(&node->v.element.attributes, attr);
        if (gAttr) {
            std::string val = std::string(gAttr->value);
            auto findIter = std::find(values.begin(), values.end(), val);
            if (findIter == values.end())
                values.push_back(val);
            return;
        }
    }

    GumboVector* children = &node->v.element.children;
    for (unsigned int i = 0; i < children->length; ++i)
        getAttributeValue(static_cast<GumboNode*>(children->data[i]), tag, attr, values);
}

bool GumboUtils::isImageLink(const std::string& link)
{
    return link.find("photo") != std::string::npos;
}

bool GumboUtils::isVideoLink(const std::string& link)
{
    return link.find("video") != std::string::npos;
}



} // namespace Jcr
